package GeneradorCodigo;

import TablaSimbolos.TabladeSimbolos;
import TablaSimbolos.entradaClase;
import TablaSimbolos.entradaMetodo;
import TablaSimbolos.entradaParametro;
import TablaSimbolos.entradaVariable;
import TablaSimbolos.parametro;
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

public class generador {

    public static PrintStream archivo, archivosalida;
    private int ultetiq = 0;  //mantiene el nro de la ultima etiqueta creada
    private int lineas = 0; // mantiene el numero de lineas generadas
    private BufferedReader br;
    private String nameFile;
    private TabladeSimbolos TS;
    private Integer numsIns = 0;
    private FileWriter salida;
    //Cada vez que se agregar una instruccion que tenga el ofset o label pendiente
    //se agrega esta instruccion a la lista de instrucicones pendientes con el mismo key
    //que tiene en la lista de instrucciones
    private LinkedHashMap<String, instruccion> instrucciones = new LinkedHashMap();
    private LinkedHashMap<String, instruccionPendiente> instruccionesPendientes = new LinkedHashMap();
    private static String lmalloc = "lmalloc";

    /**
     * Este método crea el archivo de salida
     * @param fichero Nombre del archivo que ingreso el usuario.Debe ser un nombre valido sino genera una excepción
     */
    private void crearFichero(String fichero) {
        File f = new File(fichero);
        this.borrarBackup(f);
        System.out.println(fichero);
        try {
            salida = new FileWriter(fichero, true);
            salida.flush();
        }//try
        catch (IOException ioe) {
            ioe.printStackTrace();
        }//catch
    }//crearFichero

    public generador(TabladeSimbolos t, String archivoSalida) throws IOException {
        TS = t;
        this.crearFichero(archivoSalida);
    }

    public void invocarCtorMain(){
        instruccionPendiente iP = new instruccionPendiente();
        iP.setCantVariables(true);
        iP.setMasCantidad(1);
        iP.setNombreClase("Main");
        etiqueta("newMain"); 
        rMem(1);
        pushPendiente(iP);
        pushLabel(generador.lmalloc);
        call();
        duplicarTope();        
        pushLabel("LVTMain");
        storeRef(0);
        duplicarTope();
        pushLabel("lMainMain");
        call();
        ret(0);
        
        
    };
    /**
     * Crea las rutinas heapinit y malloc pasando estas instrucciones al 
     * arreglo de instrucciones
     */
    public void inicializarInstrucciones() {
        code();
        pushLabel("lheapinit");
        call();
        pushLabel("newMain");
        call();
        halt();
        comentario("<-------- Inicio Rutinas lheapinit-lmalloc -------->");
        etiqueta("lheapinit");
        ret(0);
        comentario("Inicializacion del heap");
        comentario("<-------- fin lheapinit -------->");
        etiqueta(generador.lmalloc);
        loadFP();//Inicializacion unidad
        loadSP();
        storeFP();//Finaliza inicializacion del RA
        loadHl();
        duplicarTope();
        push(1);
        add();
        comentario("Guarda el resultado (puntero a base del bloque)");
        store(4);
        load(3);
        add();
        comentario("Mueve el heap limit (hl)- Expande el heap");
        storeHL();
        storeFP();
        comentario("Retorna eliminando el parametro");
        ret(1);
        comentario("<-------- fin lmalloc -------->");
        invocarCtorMain();
    }

    /**
     * traducirString(String cadena): genera las instrucciones en CeIASM para manipular 
     * String recibiendo como parametro cadena
     * @param cadena recibe un argumento de tipo string y genera las instrucciones
     * necesarias para el tratamiento de strings.
     * Verificacion: ok
     */
    public void traducirString(String cadena) {

        int longitud; //indica la longitud de la cadena
        longitud = cadena.length()-1;
        cadena = cadena.substring(1, longitud);
        longitud = cadena.length() + 1;	//calculo la longitud de la cadena y le sumo 1 por el terminador del string
        rMem2(1,"Celda para la referencia al CIR del String");
        push2(longitud,"Longitud del CIR para el String "+cadena); 
        pushLabel2(generador.lmalloc,"    ;Aloja el CIR para el String(crea un String)");        
        call();
        //Se inicializan los caracteres uno a uno
        for (int i = 0; i <= longitud - 2; i++) {
            duplicarTope2();
            pushLabel("\'" + cadena.charAt(i) + "\'");
            storeRef(i);            
            lineas = lineas + 3;
        }
        duplicarTope2();
        push(0);   
        storeRef2(longitud-1,"      ;Agrega el terminador para el String");        
        lineas = lineas + 6;
    }
    public void generarCompilado() throws IOException, Exception {
        Map.Entry e;
        resolverPendientes();
        for (Map.Entry entry : this.instrucciones.entrySet()) {
            instruccion i = this.instrucciones.get((String) entry.getKey());
            if (i.getLabel() != null) {
                salida.write(i.getLabel());
            }
            if (i.getInstruccion() != null) {
                salida.write(" " + i.getInstruccion());
            }
            for (int pos = 0; pos < i.getParametros().size(); pos++) {
                salida.write(" " + i.getParametros().get(pos).getValor());
            }
            if (i.getComentario() != null) {
                salida.write(i.getComentario());
            }
            salida.write("\n");

        }
        salida.close();
    }

    public void mostrarInstruccionesGeneradas() {
        Iterator iter = this.instrucciones.entrySet().iterator();
        Map.Entry e;

        for (Map.Entry entry : this.instrucciones.entrySet()) {
            //Esto recorre todos los metodos de la clase "clase".

            instruccion i = this.instrucciones.get((String) entry.getKey());
            System.out.println("--------------------------------------------");
            System.out.print("NRO: " + (String) entry.getKey());
            System.out.print("    Label: " + i.getLabel());
            System.out.print("  Instruccion : " + i.getInstruccion());
            for (int pos = 0; pos < i.getParametros().size(); pos++) {
                System.out.print("  parametros " + pos + 1 + ": " + i.getParametros().get(pos).getValor());
            }
            System.out.println("    " + i.getComentario());
            System.out.println("--------------------------------------------");
        }


    }
    //------Tareas generales---

    public void storeHL(){
        instruccion i = new instruccion();
        i.setInstruccion("STOREHL");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void pop(){
        instruccion i = new instruccion();
        i.setInstruccion("POP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    /*
     * genera una instruccion LOAD offset
     */
    public void load(int ofsset){
        instruccion i = new instruccion();
        i.setInstruccion("LOAD");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(ofsset);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    
    /*
     * genera una instruccion STORE ofsset
     *
     */
    public void store(int ofsset){
        instruccion i = new instruccion();
        i.setInstruccion("STORE");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(ofsset);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    /*
     * agrego un ADD a la pila;
     */
    public void add(){
        this.sumar();
    }
    
    public void push(int parametro) {
        instruccion i = new instruccion();
        i.setInstruccion("PUSH");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(parametro);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void push2(int parametro, String comment) {
        instruccion i = new instruccion();
        i.setInstruccion("PUSH");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(parametro);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("   ;"+comment);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    /*
     * crea insruccion LOADHL
     */
    public void loadHl() {
        instruccion i = new instruccion();
        i.setInstruccion("LOADHL");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /* Crea una etiqueta
     * 
     */
    public void etiqueta(String etiqueta) {
        instruccion i = new instruccion();
        i.setLabel(etiqueta + ":");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);

    }

    /*
     * agrega una linea de comentario
     * 
     */
    public void comentario(String comentario) {
        instruccion i = new instruccion();
        i.setComentario("; " + comentario);
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /*
     * detiene la ejecucion
     * 
     */
    public void halt() {
        instruccion i = new instruccion();
        i.setInstruccion("HALT");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /*
     * Push de un label en la pila
     * 
     */
    public void pushLabel(String label) {
        instruccion i = new instruccion();
        i.setInstruccion("PUSH");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(label);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void pushLabel2(String label, String comment) {
        instruccion i = new instruccion();
        i.setInstruccion("PUSH");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(label);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario(comment);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /*
     * genero codigo para .CODE
     */
    public void code() {
        instruccion i = new instruccion();
        i.setInstruccion(".CODE");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /*
     * genero codigo para .DATA
     */
    public void data() {
        instruccion i = new instruccion();
        i.setInstruccion(".DATA");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    public void ret(int n) {
        instruccion i = new instruccion();
        i.setInstruccion("RET");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        Integer aux = new Integer(n);
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

     /*
     * guarda el enlace dinamico del RA del llamador
     * 
     */
    public void loadFP() {
        instruccion i = new instruccion();
        i.setInstruccion("LOADFP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("; guarda enlace dinamico del RA");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /*
     * Apila el lugar donde comienza el RA de la unidad llamada
     * 
     */
    public void loadSP() {
        instruccion i = new instruccion();
        i.setInstruccion("LOADSP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("; apila donde comienza el RA del llamado");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    /*
     * Actualiza el FP con valor del tope de la pila
     * 
     */
    public void storeFP() {
        instruccion i = new instruccion();
        i.setInstruccion("STOREFP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("; actualiza el FP");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    //call deja almacenado el puntero de retorno
    public void call() {
        instruccion i = new instruccion();
        i.setInstruccion("CALL");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("; invocacion a metodo");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void intercambiarTope() {
        instruccion i = new instruccion();
        i.setInstruccion("SWAP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("; Intercambio tope de pila");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void duplicarTope() {
        instruccion i = new instruccion();
        i.setInstruccion("DUP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("; Duplico el Tope de pila");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void duplicarTope2() {
        instruccion i = new instruccion();
        i.setInstruccion("DUP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);       
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    public void loadRef(int offset) {
        instruccion i = new instruccion();
        i.setInstruccion("LOADREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(offset);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("; cargo la referencia con offset " + offset);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void loadRefMetodoPendiente(String nombreClase, String nombreMetodo) {
        instruccionPendiente iP = new instruccionPendiente();
        instruccion i = new instruccion();
        i.setInstruccion("LOADREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        iP.setNombreClase(nombreClase);
        iP.setNombreMetodo(nombreMetodo);
        i.setComentario("; cargo la referencia al codigo del metodo");
        numsIns++;
        iP.setInstruccion(i);
        instrucciones.put(numsIns.toString(), i);
        instruccionesPendientes.put(numsIns.toString(), iP);
    }

    public void loadRefMetodo(int offsetMetodo) {
        instruccion i = new instruccion();
        i.setInstruccion("LOADREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(offsetMetodo);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("; cargo la referencia al codigo del metodo");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public instruccion crearInstData() {
        instruccion i = new instruccion();
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setLabel("");
        i.setInstruccion(".DATA");
        i.setParametros(arrayParam);
        i.setComentario(";Definicion de datos");
        return i;
    }
    
    public void rMem(int cant){
        instruccion i = new instruccion();
        i.setInstruccion("RMEM");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(cant);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("; Reservamos memoria para el resultado del Malloc");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void rMem2(int cant, String comment){
        instruccion i = new instruccion();
        i.setInstruccion("RMEM");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(cant);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("   ;"+comment);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    public void pushPendiente(instruccionPendiente iP){
        instruccion i = new instruccion();
        i.setInstruccion("PUSH");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
        instruccionesPendientes.put(numsIns.toString(), iP);
    }
    
    public void storeRef(int offset){
        instruccion i = new instruccion();
        i.setInstruccion("STOREREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(offset);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void storeRef2(int offset, String comment){
        instruccion i = new instruccion();
        i.setInstruccion("STOREREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        Integer aux = new Integer(offset);
        p.setValor(aux.toString());
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario(comment);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    //Fin tareas Generales
    //------------MEtodods-----
    public void valorRetornoIntercambioThis() {
        //reserva espacio para el valor de retorno
        reservarEspacioValorRetorno();
        intercambiarTope();
        //Intercambia tope de pila ( objeto this en el tope de pila)
    }

    public void reservarEspacioValorRetorno() {
        instruccion i = new instruccion();
        i.setInstruccion("RMEM");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor("1");
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("; Reservo espacio para el valor de retorno");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    /*Implemento todos las traducciones de la clase system*/
        public void sysRead(){
            instruccion i = new instruccion();
            i.setInstruccion("READ");
            ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }
        
        public void sysPrintB(){
            instruccion i = new instruccion();
            i.setInstruccion("BPRINT");
            ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }
        
        public void sysPrintC(){
            instruccion i = new instruccion();
            i.setInstruccion("CPRINT");
            ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }
        
        public void sysPrintI(){
            instruccion i = new instruccion();
            i.setInstruccion("IPRINT");
            ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }
        
        public void sysPrintS(){
            instruccion i = new instruccion();
            i.setInstruccion("SPRINT");
            ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }
        
        public void sysPrintLn(){
            instruccion i = new instruccion();
            i.setInstruccion("PRNLN");
            ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }
    /*Fin de las traducciones de la clase system*/
    
    

    private int darNroMetodoSystem(String metodosystem){
    int val=0;
        if (metodosystem.compareTo("read")==0){val=1;}
        if (metodosystem.compareTo("printB")==0){val=2;}
        if (metodosystem.compareTo("printC")==0){val=3;}
        if (metodosystem.compareTo("printI")==0){val=4;}
        if (metodosystem.compareTo("printS")==0){val=5;}
        if (metodosystem.compareTo("println")==0){val=6;}
        if (metodosystem.compareTo("printBln")==0){val=7;}
        if (metodosystem.compareTo("printCln")==0){val=8;}
        if (metodosystem.compareTo("printIln")==0){val=9;}
        if (metodosystem.compareTo("printSln")==0){val=10;}
    return val;
    }
    public void invocarMetodo(String nomVar, String nombreClase, String nombreMetodo) throws Exception {
        int choice;
        if (nombreClase.compareTo("System")==0){
            choice=darNroMetodoSystem(nombreMetodo);
            switch (choice){
                    case 1:{
                        sysRead();
                        break;
                    }
                    case 2:{
                        sysPrintB();
                        break;                     
                    }
                    case 3:{
                        sysPrintC();
                        break;
                    }
                    case 4:{
                        sysPrintI();
                        break;
                    }
                    case 5:{
                        sysPrintS();
                        break;
                    }
                    case 6:{
                        sysPrintLn();
                        break;
                    }
                    case 7:{
                        sysPrintB();
                        sysPrintLn();
                        break;
                    }
                    case 8:{
                        sysPrintC();
                        sysPrintLn();
                        break;
                    }
                    case 9:{
                        sysPrintI();
                        sysPrintLn();
                        break;
                    }
                    case 10:{
                        sysPrintS();
                        sysPrintLn();
                        break;
                    }
            }
        }
        else{
        entradaClase eC = TS.darClase(nombreClase);
        entradaMetodo eM = eC.darMetodo(nombreMetodo);
        int offsetMetodo = eM.darOffsetMethod();
        //cargo en la pila la VT del objeto que esta en el tope de la pila
        loadRef(0);
        if (offsetMetodo < 0) {
            loadRefMetodoPendiente(nombreClase, nombreMetodo);
        } else {
            loadRefMetodo(offsetMetodo);
        }
        //call deja almacenado el puntero de retorno
        call();
       }
    }
    
    
    public void invocarMetodo(String nombreClase, String nombreMetodo) throws Exception {
        int choice;
        if (nombreClase.compareTo("System")==0){
            choice=darNroMetodoSystem(nombreMetodo);
            switch (choice){
                    case 1:{
                        sysRead();
                        break;
                    }
                    case 2:{
                        sysPrintB();
                        break;                     
                    }
                    case 3:{
                        sysPrintC();
                        break;
                    }
                    case 4:{
                        sysPrintI();
                        break;
                    }
                    case 5:{
                        sysPrintS();
                        break;
                    }
                    case 6:{
                        sysPrintLn();
                        break;
                    }
                    case 7:{
                        sysPrintB();
                        sysPrintLn();
                        break;
                    }
                    case 8:{
                        sysPrintC();
                        sysPrintLn();
                        break;
                    }
                    case 9:{
                        sysPrintI();
                        sysPrintLn();
                        break;
                    }
                    case 10:{
                        sysPrintS();
                        sysPrintLn();
                        break;
                    }
            }
        }
        else{
        entradaClase eC = TS.darClase(nombreClase);
        entradaMetodo eM = eC.darMetodo(nombreMetodo);
        int offsetMetodo = eM.darOffsetMethod();
        //cargo en la pila la VT del objeto que esta en el tope de la pila
        loadRef(0);
        if (offsetMetodo < 0) {
            loadRefMetodoPendiente(nombreClase, nombreMetodo);
        } else {
            loadRefMetodo(offsetMetodo);
        }
        //call deja almacenado el puntero de retorno
        call();
       }
    }
    /*
     * los constructores se pueden invocar desde cualquier clase;
     */
    public void invocarConstructorClase(String nombreClase) throws Exception{
        iniciarClase(nombreClase);
    }
    /*
     * Acomoda el Registro de activacion de la unidad llamada
     * 
     */
    public void inicializoCuerpoMetodo(String nombreClase ,String nombreMetodo) throws Exception {
        entradaClase eC= TS.darClase(nombreClase);
        entradaMetodo eM = eC.darMetodo(nombreMetodo);
        String etiqueta = eM.darEtiqueta();
        //code();
        etiqueta(etiqueta);
        loadFP();
        loadSP();
        storeFP();
    }

    public void finalizarCuerpoMetodo(String nombreClase, String nombreMetodo) throws Exception {
        entradaClase eC = TS.darClase(nombreClase);
        entradaMetodo eM = eC.darMetodo(nombreMetodo);
        //+1 por el THIS
        int tamañoRA = eM.getTablaParametros().size();
        store(tamañoRA + 4);
        //almacendo el valor de retorno hay que hacer el retorno de la unidad
        storeFP();
        ret(tamañoRA + 1);
    }

    //-----------Fin Metodos---
    //Sin terminar
    public void ponerVariableEnPila(String identificador) throws Exception {
        //esta variable puede ser un parametro o una variable de instancia
        //realizar las operaciones de carga de variable. Un LOAD o LOADREF
        entradaClase eC;
        entradaMetodo eM;
        entradaParametro eP;
        entradaVariable eV;
        int offsetParametro,offset;
        if(TS.esParametro(identificador)){
            eC = TS.darClase(TS.claseActual());
            eM = eC.darMetodo(TS.getMetodoActual());
            eP = eM.darParametro(identificador);
            if(eP != null){
                offsetParametro = eP.darOffsetParametro();
            } 
            else{
                throw new Exception("No se puede encontrar el parametro " + identificador + "al generar el codigo");
            }
            offset = (eM.darCantParametros() + 3) - (eP.darOffsetParametro() );
            //throw new Exception("cantPArametros: " + eM.darCantParametros()+ "-OffsetParametro: " + eP.darOffsetParametro() + "genero un LOAD:" + offset + " metodo : " +TS.getMetodoActual());
            load(offset);
        }
        else{
            if(TS.esVarInstancia(identificador)){
                load(3); //apilo el CIR de THIS
                eC = TS.darClase(TS.claseActual());
                eV = eC.darVariable(identificador);
                if(eV != null){
                    offset = eV.darOffsetVarLocal();
                }
                else{
                    throw new Exception("No se puede encontrar la variable " + identificador + "al generar el codigo");
                }
                //throw new Exception("cantVariables: " + eC.getTablaVariable().size() + "-Offsetvariable: " + offset);
                loadRefVariablePendiente(identificador);
            }
            
        }
        
        /*
        instruccion i = new instruccion();
        i.setInstruccion("LOAD");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(identificador);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario(";colocar el offset no el identificador--->" + numlinea);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);*/
    }
    
    public void loadRefVariablePendiente(String identificadorVar){
        instruccionPendiente iP = new instruccionPendiente();
        instruccion i = new instruccion();
        i.setInstruccion("LOADREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        iP.setNombreClase(TS.claseActual());
        iP.setCantVariables(true);
        iP.setNombreVariable(identificadorVar);
        i.setComentario("; cargo la referencia al codigo del metodo");
        numsIns++;
        iP.setInstruccion(i);
        instrucciones.put(numsIns.toString(), i);
        instruccionesPendientes.put(numsIns.toString(), iP);
    
    }
    
    public void storeRefVariablePendiente(String nombreClase,String identificadorVar){
            instruccionPendiente iP = new instruccionPendiente();
        instruccion i = new instruccion();
        i.setInstruccion("STOREREF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        iP.setNombreClase(nombreClase);
        iP.setCantVariables(true);
        iP.setNombreVariable(identificadorVar);
        i.setComentario("; cargo la referencia al codigo del metodo");
        numsIns++;
        iP.setInstruccion(i);
        instrucciones.put(numsIns.toString(), i);
        instruccionesPendientes.put(numsIns.toString(), iP);
    }
    
    
    public void almacenarValorEnVariable(String identificadorVar) throws Exception{
        
        entradaClase eC;
        entradaMetodo eM;
        entradaParametro eP;
        entradaVariable eV;
        int offsetParametro,offset;
        if(TS.esParametro(identificadorVar)){
            eC = TS.darClase(TS.claseActual());
            eM = eC.darMetodo(TS.getMetodoActual());
            eP = eM.darParametro(identificadorVar);
            if(eP != null){
                offsetParametro = eP.darOffsetParametro();
            } 
            else{
                throw new Exception("No se puede encontrar el parametro " + identificadorVar + "al generar el codigo");
            }
            offset = (eM.darCantParametros() + 3) - (eP.darOffsetParametro() );
            //throw new Exception("cantPArametros: " + eM.darCantParametros()+ "-OffsetParametro: " + eP.darOffsetParametro() + "genero un LOAD:" + offset + " metodo : " +TS.getMetodoActual());
            duplicarTope();
            store(offset);
        }
        else{
            if(TS.esVarInstancia(identificadorVar)){
                load(3); //apilo el CIR de THIS
                eC = TS.darClase(TS.claseActual());
                eV = eC.darVariable(identificadorVar);
                if(eV != null){
                    offset = eV.darOffsetVarLocal();
                }
                else{
                    throw new Exception("No se puede encontrar la variable " + identificadorVar + "al generar el codigo");
                }
                //throw new Exception("cantVariables: " + eC.getTablaVariable().size() + "-Offsetvariable: " + offset);
                duplicarTope();
                load(3);
                intercambiarTope();
                if(offset<0){
                    storeRefVariablePendiente(TS.claseActual(), identificadorVar);
                }
                else{
                    store(offset);
                    
                }
                
            }
            
        }
        
    }
    
    

    public void ponerLiteralEnPila(String literal) {
        //realizar las operaciones de carga de variable
        instruccion i = new instruccion();
        i.setInstruccion("PUSH");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(literal);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario(";colocar el numero ");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void sumar() {
        instruccion i = new instruccion();
        i.setInstruccion("ADD");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void restar() {
        instruccion i = new instruccion();
        i.setInstruccion("SUB");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario("   ;colocar un sub");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void dividir() {
        instruccion i = new instruccion();
        i.setInstruccion("DIV");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario(";colocar un sub");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }

    public void multiplicar() {
        instruccion i = new instruccion();
        i.setInstruccion("MUL");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);
        i.setComentario(";colocar un mul");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void modulo() {
        instruccion i = new instruccion();
        i.setInstruccion("MOD");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void neg() {
        instruccion i = new instruccion();
        i.setInstruccion("NEG");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void and() {
        instruccion i = new instruccion();
        i.setInstruccion("AND");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void or() {
        instruccion i = new instruccion();
        i.setInstruccion("OR");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void not() {
        instruccion i = new instruccion();
        i.setInstruccion("NOT");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void eq() {
        instruccion i = new instruccion();
        i.setInstruccion("EQ");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void ne() {
        instruccion i = new instruccion();
        i.setInstruccion("NE");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void lt() {
        instruccion i = new instruccion();
        i.setInstruccion("LT");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void gt() {
        instruccion i = new instruccion();
        i.setInstruccion("GT");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void le() {
        instruccion i = new instruccion();
        i.setInstruccion("LE");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void ge() {
        instruccion i = new instruccion();
        i.setInstruccion("GE");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        i.setParametros(arrayParam);        
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void bt(String label) {
        instruccion i = new instruccion();
        i.setInstruccion("BT");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(label);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void bf(String label) {
        instruccion i = new instruccion();
        i.setInstruccion("BF");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(label);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    public void jump(String label) {
        instruccion i = new instruccion();
        i.setInstruccion("JUMP");
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(label);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        numsIns++;
        instrucciones.put(numsIns.toString(), i);
    }
    
    
    //Sin terminar
    public void iniciarClase(String nombreClase) throws Exception {
        //inicializar datos para la clase actual
        rMem(1);
        instruccionPendiente iP = new instruccionPendiente();
        iP.setCantVariables(true);
        iP.setNombreClase(nombreClase);
        //pendiente la reserva el numero de variables de instancia
        //esta linea va l saque solo para probar
        //pushPendiente(iP);
        push(2);
        pushLabel(generador.lmalloc);
        call();
        duplicarTope();
        entradaClase eC = TS.darClase(nombreClase);
        String VT = eC.getVT();
        pushLabel(VT);
        storeRef(0);
        entradaMetodo constructor = eC.darMetodo(nombreClase);
        pushLabel(constructor.darEtiqueta());
        call();
        ret(0);
        comentario("----------------------------------fin de inicializacion de la clase: " + nombreClase);
    }

    //Sin terminar
    public void agregarVariablesInstancia(String nombreClase) {
        //definir todas las variables presentes en la tabla de simbolos
        instruccion i = new instruccion();
        i.setInstruccion("DW");

        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p = new parametroInstruccion();
        p.setValor(nombreClase);
        arrayParam.add(p);
        i.setParametros(arrayParam);
        i.setComentario("; Lista de variables");
        numsIns++;
        instrucciones.put(numsIns.toString(), i);

    }

    public void eliminarPendientes() {
    }

    /**
     *cerrarArchivo: se encarga de cerrar el archivo que contiene las instruciones 
     * de codigo intermedio 
     */
    public void cerrarArchivo() {
        archivo.close();
    }

    /**
     * crearVT(String virtualTable): crea la VT de una clase con nombre virtualTable 
     * utilizando las instrucciones de definicion de datos
     * @param virtualTable recibe un parametro de tipo String sera la etiqueta de la VT
     * verificacion: pendiente
     */
    public void crearVT(String nombreClase) throws Exception {
        /*
        implementar: ver posibilidad de usar un vector de etiquetas
        EJ:
        .DATA
        VT A: DW lmet1, lmet2
        .CODE
        lmet1 ... {codigo de m1 de A}
        ...
         */
        //instruccion data = crearInstData();
        //numsIns++;
        //instrucciones.put(numsIns.toString(), data);
        entradaClase eC = this.TS.darClase(nombreClase);
        entradaMetodo entryMethod;
        String etiquetaMetodo;
        instruccion i;
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        parametroInstruccion p;
        String labelVT = "LVT"+ nombreClase;
        eC.setVT(labelVT);
        etiqueta(labelVT);
        Map.Entry e;
        for (Map.Entry entry : eC.getTablaMetodo().entrySet()) {
            //Esto recorre todos los metodos de la clase "clase".
            entryMethod = eC.darMetodo((String) entry.getKey());
            etiquetaMetodo = entryMethod.darEtiqueta();
            if(!entryMethod.esConstructorDeClase()){
                i = new instruccion();
                i.setInstruccion("DW");
                p = new parametroInstruccion();
                p.setValor(etiquetaMetodo);
                arrayParam = new ArrayList<parametroInstruccion>();
                arrayParam.add(p);
                i.setParametros(arrayParam);
                i.setComentario("   ;Metodo");
                numsIns++;
                instrucciones.put(numsIns.toString(), i);
                //this.darClase(clase).mostrarMetodo(entryMethod);
            }
            
        }
        if (eC.getTablaMetodo().size() == 1) {
            i = new instruccion();
            i.setInstruccion("NOP");
            arrayParam = new ArrayList<parametroInstruccion>();
            i.setParametros(arrayParam);
            i.setComentario("; Clase sin metodos");
            numsIns++;
            instrucciones.put(numsIns.toString(), i);
        }

    }

    /**
     * generarString(String cadena): genera las instrucciones en CeIASM para manipular 
     * String recibiendo como parametro cadena
     * @param cadena recibe un argumento de tipo string y genera las instrucciones
     * necesarias para el tratamiento de strings.
     * Verificacion: ok
     */
    public void generarString(String cadena) {

        int longitud; //indica la longitud de la cadena
        archivo.println("RMEM 1  ; Celda para la referencia al CIR del string");
        longitud = cadena.length() + 1;	//calculo la longitud de la cadena y le sumo 1 por el terminador del string
        archivo.println("PUSH " + longitud + "	;Longitud del CIR para el string " + cadena);
        archivo.println("PUSH lmalloc		;Aloja el CIR para el string (crea un String)");
        archivo.println("CALL");
        //Se inicializan los caracteres uno a uno
        for (int i = 0; i <= longitud - 2; i++) {
            archivo.println("DUP");
            archivo.println("PUSH " + "\'" + cadena.charAt(i) + "\'");
            archivo.println("STOREREF " + i);
            lineas = lineas + 3;
        }
        archivo.println("DUP");
        archivo.println("PUSH 0");
        archivo.println("STOREREF " + (longitud - 1) + "	;Agrega el terminador para el String");
        lineas = lineas + 6;
    }

     /**
     * borrarBackup es un metodo que se encarga de borrar el archivo temporal
     * usado para hacer la traduccion del archivo fuente al codigo intermedio
     * en CeIASM.
     * @param archbkp es el archivo temporal usado para hacer la traduccion.
     * Es eliminado una vez que se ha resuelto los offset pendientes.
     */
    public void borrarBackup(File archbkp) {
        try {
            if (archbkp.exists()) {
                if (archbkp.delete()) {
                } //no hace nada
                else {
                    System.out.println("No pude ser borrado el archivo temporal");
                }
            }
        } catch (Exception ex) {
            //Captura un posible error y le imprime en pantalla   
            System.out.println(ex.getMessage());

        }
    }
    public void resolverPendientes() throws Exception{
        Map.Entry e;
        for (Map.Entry entry : this.instruccionesPendientes.entrySet()) {
            instruccion i = this.instrucciones.get((String) entry.getKey());
            instruccionPendiente iP = instruccionesPendientes.get((String) entry.getKey());
            reeemplazarInstruccionEnPos((String) entry.getKey(),i,iP);
        }
    }
    public void reeemplazarInstruccionEnPos(String key,instruccion i,instruccionPendiente iP) throws Exception{
        entradaClase eC;
        entradaVariable eV;
        entradaMetodo eM;
        int offset;
        parametroInstruccion pI;
        Integer aux;
        ArrayList<parametroInstruccion> arrayParam = new ArrayList<parametroInstruccion>();
        if(iP.isCantVariables()){
            eC = TS.darClase(iP.getNombreClase());
            offset = eC.getTablaVariable().size();
            pI = new parametroInstruccion();
            int valor = offset + iP.getMasCantidad();
            aux = new Integer(offset + iP.getMasCantidad() );
            pI.setValor(aux.toString());
            arrayParam.add(pI);
            i.setParametros(arrayParam);
            i.setComentario("; offset Resuelto. " + valor);
            instrucciones.put(key, i);
        }
        else{
                if(iP.getNombreMetodo().equals("$") && !iP.getNombreVariable().equals("$")){
                //Voy a buscar el desplazamiento de la variable de instancia
                eC = TS.darClase(iP.getNombreClase());
                eV = eC.darVariable(iP.getNombreVariable());
                offset =eV.darOffsetVarLocal();
                pI = new parametroInstruccion();
                int valor = offset + iP.getMasCantidad();
                aux = new Integer(offset + iP.getMasCantidad() );
                pI.setValor(aux.toString());
                arrayParam.add(pI);
                i.setParametros(arrayParam);
                i.setComentario("; offset Resuelto. " + valor);
                instrucciones.put(key, i);
            }
            else{
                eC=TS.darClase(iP.getNombreClase());
                eM=eC.darMetodo(iP.getNombreMetodo());
                offset = eM.darOffsetMethod();
                pI = new parametroInstruccion();
                int valor = offset + iP.getMasCantidad();
                aux = new Integer(offset + iP.getMasCantidad() );
                pI.setValor(aux.toString());
                arrayParam.add(pI);
                i.setParametros(arrayParam);
                i.setComentario("; offset Resuelto. " + valor);
                instrucciones.put(key, i);
            }
        }
        
        
    }

}